﻿//    nVLC
//    
//    Author:  Roman Ginzburg
//
//    nVLC is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    nVLC is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//    GNU General Public License for more details.
//     
// ========================================================================

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Declarations;
using LibVlcWrapper;

namespace Implementation
{
   internal class Log : DisposableBase
   {
      Thread m_reader;
      IntPtr m_hLog = IntPtr.Zero;
      volatile bool doRun;
      ILogger m_logger;
      bool m_enabled;
      LogIterator m_logIterator;

      public Log(IntPtr hLib, ILogger logger)
      {
         m_hLog = LibVlcMethods.libvlc_log_open(hLib);
         m_logger = logger;

         LibVlcMethods.libvlc_set_log_verbosity(m_hLog, 2);
         m_logIterator = new LogIterator(m_hLog);
         m_reader = new Thread(Retreive);
         m_reader.IsBackground = true;

         WriteTimeout = 1000;
      }

      public int WriteTimeout { get; set; }

      private void Retreive()
      {
         while (doRun)
         {
            foreach (var item in m_logIterator)
            {
               switch (item.Severity)
               {
                  case libvlc_log_messate_t_severity.INFO:
                     m_logger.Info(item.Message);
                     break;

                  case libvlc_log_messate_t_severity.WARN:
                     m_logger.Warning(item.Message);
                     break;

                  case libvlc_log_messate_t_severity.DBG:
                     m_logger.Debug(item.Message);
                     break;

                  case libvlc_log_messate_t_severity.ERR:
                     
                  default:
                     m_logger.Error(item.Message);
                     break;
               }
            }   

            Thread.Sleep(WriteTimeout);
         }
      }

      private void Start()
      {
         doRun = true;
         m_reader.Start();
      }

      private void Stop()
      {
         doRun = false;
      }

      public bool Enabled
      {
         get
         {
            return m_enabled;
         }
         set
         {
            if (m_enabled == value)
            {
               return;
            }

            m_enabled = value;
            if (m_enabled)
            {
               Start();
            }
            else
            {
               Stop();
            }
         }
      }

      protected override void Dispose(bool disposing)
      {
         LibVlcMethods.libvlc_log_close(m_hLog);
      }

      private class LogIterator : IEnumerable<LogMessage>
      {
         IntPtr m_hLog;

         internal LogIterator(IntPtr hLog)
         {
            m_hLog = hLog;
         }

         #region IEnumerable<string> Members

         public IEnumerator<LogMessage> GetEnumerator()
         {
            IntPtr i = LibVlcMethods.libvlc_log_get_iterator(m_hLog);

            while (LibVlcMethods.libvlc_log_iterator_has_next(i) != 0)
            {
               libvlc_log_message_t msg = new libvlc_log_message_t();
               msg.sizeof_msg = (uint)Marshal.SizeOf(msg);
               LibVlcMethods.libvlc_log_iterator_next(i, ref msg);
               
               yield return GetMessage(msg);
            }

            LibVlcMethods.libvlc_log_iterator_free(i);
            LibVlcMethods.libvlc_log_clear(m_hLog);
         }

         private LogMessage GetMessage(libvlc_log_message_t msg)
         {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("{0} ", Marshal.PtrToStringAnsi(msg.psz_header));
            sb.AppendFormat("{0} ", Marshal.PtrToStringAnsi(msg.psz_message));
            sb.AppendFormat("{0} ", Marshal.PtrToStringAnsi(msg.psz_name));
            sb.Append(Marshal.PtrToStringAnsi(msg.psz_type));

            return new LogMessage() { Message = sb.ToString(), Severity = (libvlc_log_messate_t_severity)msg.i_severity };
         }

         #endregion

         #region IEnumerable Members

         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
         {
            return this.GetEnumerator();
         }

         #endregion
      }

      private struct LogMessage
      {
         public libvlc_log_messate_t_severity Severity;
         public string Message;
      }
   }
}
